package fgin

import (
	"context"
	"fmt"
	"strings"
	"time"

	"gitee.com/wu-jin-feng/fgin/fgin_plugs/fotel/ftracer"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// v1.1.3版本增加自定义日志注册 -> 推送到1.1.5
var (
	logger           *zap.Logger
	logSugerBase     *zap.SugaredLogger
	logSuger         *FginSugaredLogger
	disableFginSuger bool                             // 是否禁用执行原生的日志接口 true 禁止 false 不禁止
	registerLoggers  = []FginSugaredLoggerInterface{} // 注册的自定义日志接口
)

// v2版本 兼容可以按时间分文件
func logInit() {
	defaultLogConfig := Log{
		Level:     "info",
		WriteType: 1,
	}
	if fginConfig.Log == (Log{}) {
		// 如果用户没有填写log配置,不进行初始化,使用默认配置进行初始化
		fginConfig.Log = defaultLogConfig
	}
	loggerConfig := zap.Config{}
	switch fginConfig.Env {
	case "pro":
		loggerConfig = zap.NewProductionConfig()
	case "dev":
		loggerConfig = zap.NewDevelopmentConfig()
	default:
		panic("日志初始化失败, 请检查配置env是否为: dev或pro")
	}
	// 取消抽样，日志完整输出
	loggerConfig.Sampling = nil
	// 设置输出格式, 需为： console 、 json
	if ArrayFindString([]string{"console", "json"}, fginConfig.Log.Encoding) {
		loggerConfig.Encoding = fginConfig.Log.Encoding
	}
	// 统一格式化时间
	loggerConfig.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
	logLv := zap.NewAtomicLevelAt(zap.InfoLevel)
	switch fginConfig.Log.Level {
	case "debug":
		logLv = zap.NewAtomicLevelAt(zap.DebugLevel)
	case "info":
		logLv = zap.NewAtomicLevelAt(zap.InfoLevel)
	case "warn":
		logLv = zap.NewAtomicLevelAt(zap.WarnLevel)
	case "error":
		logLv = zap.NewAtomicLevelAt(zap.ErrorLevel)
	}
	loggerConfig.Level = logLv

	switch fginConfig.Log.WriteType {
	case 2:
		logPathDir := strings.Join(DelArrItemStr(strings.Split(fginConfig.Log.LogPath, "/"), -1), "/")
		// 目录不存在则创建
		if !FileOrDirExists(logPathDir) {
			if err := MakeDirAll(logPathDir); err != nil {
				panic("日志目录不存在进行创建,创建失败:" + err.Error())
			}
		}
		// 打印到文件中
		if fginConfig.Log.LogPath == "" {
			panic("日志输入为文件,文件地址没有配置")
		}
		if fginConfig.Log.Rotate != "" {
			// 分文件
			fSugaredLoggerInit(&loggerConfig)
			return
		} else {
			// 单文件
			loggerConfig.OutputPaths = []string{fginConfig.Log.LogPath}
			loggerConfig.ErrorOutputPaths = []string{fginConfig.Log.LogPath}
		}
	}
	var err error
	logger, err = loggerConfig.Build()
	if err != nil {
		panic("创建日志对象失败:" + err.Error())
	}
	// 创建基础log对象
	logSugerBase = logger.Sugar()
	logSuger = &FginSugaredLogger{
		logSugerBase: logSugerBase,
		loggerConfig: &loggerConfig,
		otherLoggers: registerLoggers,
	}
}

// 增加日志分割
func fSugaredLoggerInit(loggerConfig *zap.Config) {
	// 更新fgin log配置
	logSuger = &FginSugaredLogger{
		logSugerBase: logSugerBase,
		loggerConfig: loggerConfig,
		otherLoggers: registerLoggers,
	}
	if !ArrayFindString([]string{"y", "M", "d", "h", "m", "s"}, fginConfig.Log.Rotate) {
		panic("日志rotate配置不正确")
	}
	checkLogPath()
}

// 检查日志文件分割path
func checkLogPath() {
	// 如果没有配置rotate，或无需记录文件，直接返回
	if fginConfig.Log.Rotate == "" || fginConfig.Log.WriteType != 2 {
		return
	}
	// 获取标识
	rotateFlag := ""
	switch fginConfig.Log.Rotate {
	case "y":
		rotateFlag = time.Now().Format("2006")
	case "M":
		rotateFlag = time.Now().Format("200601")
	case "d":
		rotateFlag = time.Now().Format("20060102")
	case "h":
		rotateFlag = time.Now().Format("2006010215")
	case "m":
		rotateFlag = time.Now().Format("200601021504")
	case "s":
		rotateFlag = time.Now().Format("20060102150405")
	default:
		panic("日志rotate配置不正确")
	}
	if logSuger.rotateFlag == rotateFlag {
		// 无需切换文件或记录文件，直接返回
		return
	}
	logSuger.rotateFlag = rotateFlag
	// 根据配置创建日志文件路径
	logPathLs := strings.Split(fginConfig.Log.LogPath, "/")
	logPathFile := strings.Split(logPathLs[len(logPathLs)-1], ".")[0]
	logPathLs = append(DelArrItemStr(logPathLs, -1), logPathFile)
	logSuger.loggerConfig.OutputPaths = []string{fmt.Sprintf("%v.%v.log", strings.Join(logPathLs, "/"), rotateFlag)}
	logSuger.loggerConfig.ErrorOutputPaths = []string{fmt.Sprintf("%v.%v.log", strings.Join(logPathLs, "/"), rotateFlag)}
	// 先关闭原来的logger
	if logger != nil {
		logger.Sync()
	}
	var err error
	logger, err = logSuger.loggerConfig.Build()
	if err != nil {
		panic("创建fgin日志对象失败:" + err.Error())
	}
	logSugerBase = logger.Sugar()
	logSuger.logSugerBase = logger.Sugar()
}

// 接口，用于用户自定义实现日志
type FginSugaredLoggerInterface interface {
	Debug(args ...interface{})
	Info(args ...interface{})
	Warn(args ...interface{})
	Error(args ...interface{})
}

// 注册自定义方法方法
func RegisterLoggers(loggers []FginSugaredLoggerInterface, disable bool) {
	registerLoggers = loggers
	disableFginSuger = disable
}

// 获取是否禁止原生日志输出
func GetDisableFginSuger() bool {
	return disableFginSuger
}

// 原生实现的日志接口
type FginSugaredLogger struct {
	logSugerBase *zap.SugaredLogger
	loggerConfig *zap.Config
	rotateFlag   string                       // 分割标识，年月日时分秒
	otherLoggers []FginSugaredLoggerInterface // 用户注册实现的自定义日志
}

func (s *FginSugaredLogger) Debug(args ...interface{}) {
	if !GetDisableFginSuger() {
		checkLogPath()
		args = getLogArgsTraceId(args)
		s.logSugerBase.WithOptions(zap.AddCallerSkip(1)).Debug(args...)
	}
	// 执行自定义日志
	for _, logger := range s.otherLoggers {
		logger.Debug(args...)
	}
}
func (s *FginSugaredLogger) Info(args ...interface{}) {
	if !GetDisableFginSuger() {
		checkLogPath()
		args = getLogArgsTraceId(args...)
		s.logSugerBase.WithOptions(zap.AddCallerSkip(1)).Info(args...)
	}
	// 执行自定义日志
	for _, logger := range s.otherLoggers {
		logger.Info(args...)
	}
}

func (s *FginSugaredLogger) Warn(args ...interface{}) {
	if !GetDisableFginSuger() {
		checkLogPath()
		args = getLogArgsTraceId(args...)
		s.logSugerBase.WithOptions(zap.AddCallerSkip(1)).Warn(args...)
	}
	// 执行自定义日志
	for _, logger := range s.otherLoggers {
		logger.Warn(args...)
	}
}

func (s *FginSugaredLogger) Error(args ...interface{}) {
	if !GetDisableFginSuger() {
		checkLogPath()
		args = getLogArgsTraceId(args...)
		s.logSugerBase.WithOptions(zap.AddCallerSkip(1)).Error(args...)
	}
	// 执行自定义日志
	for _, logger := range s.otherLoggers {
		logger.Error(args...)
	}
}

// 补充日志的traceId，配置otel 链路追踪可获得
func getLogArgsTraceId(args ...any) []any {
	var (
		ctx   context.Context
		isOk  bool
		index int
	)
	// 遍历获取ctx
	for i, arg := range args {
		v, ok := arg.(*gin.Context)
		if ok {
			ctx = v.Request.Context()
			index = i
			isOk = true
		}
		v1, ok1 := arg.(context.Context)
		if ok1 {
			ctx = v1
			index = i
			isOk = true
		}
		if isOk {
			break
		}
	}
	if !isOk {
		return args
	}
	// 有获取到ctx
	newArgs := []any{}
	traceId := ftracer.GetTraceIdFromContext(ctx)
	if isOk {
		newArgs = append(newArgs, fmt.Sprintf("[traceId:%s]", traceId))
	} else {
		newArgs = append(newArgs, "[traceId:]")
	}
	newArgs = append(newArgs, args[0:index]...)
	newArgs = append(newArgs, args[index+1:]...)
	return newArgs
}
